import importlib
import os
import sys
from typing import Union

from PyQt5.QtCore import QDir, Qt, QEvent
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import (
    QAbstractItemView,
    QApplication,
    QFileSystemModel,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QTextEdit,
    QToolTip,
    QTreeView,
    QVBoxLayout,
    QWidget,
    QFileDialog,
)
from qt_material import apply_stylesheet
from classes.album import Album

from classes.track import FlacTrack, MP3Track


class TrackLabel(QLabel):
    def __init__(self, value: str, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.installEventFilter(kwargs["parent"])
        self.select = False
        self.value = value
        self.setFixedSize(kwargs["width"], 30)
        self.setAlignment((Qt.AlignHCenter | Qt.AlignVCenter))


class TrackEdit(QLineEdit):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.select = False


class TrackText(QTextEdit):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.select = False
        self.setAcceptRichText(False)


class LabelLinePair(QHBoxLayout):
    def __init__(self, **kwargs):
        super().__init__()
        self.lb = TrackLabel(
            text=kwargs["text"],
            parent=kwargs["parent"],
            width=kwargs["width"],
            value=kwargs["value"],
        )
        self.edit = TrackEdit(parent=kwargs["parent"])
        self.lb.edit = self.edit
        if "stretch" in kwargs and "alignment" in kwargs:
            self.addWidget(
                self.lb, stretch=kwargs["stretch"], alignment=kwargs["alignment"]
            )
            self.addWidget(self.edit, stretch=kwargs["stretch"] + 1)
        else:
            self.addWidget(self.lb)
            self.addWidget(self.edit)

    def getValue(self) -> str:
        return self.lb.value

    def getText(self) -> str:
        return self.edit.text()

    def updateText(self, info) -> None:
        if not self.lb.select:
            if info != self.edit.text() and self.edit.text() != "":
                self.edit.setText("< various >")
            else:
                self.edit.setText(info)

    def clear(self) -> None:
        if not self.lb.select:
            self.edit.clear()


class LabelTextPair(QHBoxLayout):
    def __init__(self, **kwargs):
        super().__init__()

        self.col1 = QVBoxLayout()
        self.col2 = QVBoxLayout()

        self.lb = TrackLabel(
            text=kwargs["text"],
            parent=kwargs["parent"],
            width=kwargs["width"],
            value=kwargs["value"],
        )
        self.edit = TrackText(parent=kwargs["parent"])
        self.lb.edit = self.edit

        self.col1.addWidget(self.lb, stretch=1, alignment=Qt.AlignTop)
        self.col2.addWidget(self.edit)

        self.addLayout(self.col1)
        self.addLayout(self.col2)

    def getValue(self) -> str:
        return self.lb.value

    def getText(self) -> str:
        return self.edit.toPlainText()

    def updateText(self, info):
        if not self.lb.select:
            if info != self.edit.toPlainText() and self.edit.toPlainText() != "":
                self.edit.setText("< various >")
            else:
                self.edit.setText(info)

    def clear(self) -> None:
        if not self.lb.select:
            self.edit.clear()


class TrackGroupBox(QGroupBox):
    tracks: list[Union[FlacTrack, MP3Track]]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tracks = None

        self.generalLayout = QVBoxLayout()

        self.cols = QHBoxLayout()
        self.col1 = QVBoxLayout()

        self.row1 = QHBoxLayout()
        self.track_number = LabelLinePair(
            text="Track №", parent=self, width=50, value="tracknumber"
        )
        self.total_tracks = LabelLinePair(
            text="/", parent=self, width=10, value="tracktotal"
        )
        self.disk_number = LabelLinePair(
            text="Disk", parent=self, width=30, value="discnumber"
        )
        self.total_disks = LabelLinePair(
            text="/", parent=self, width=10, value="disctotal"
        )
        self.row1.addLayout(self.track_number)
        self.row1.addLayout(self.total_tracks)
        self.row1.addLayout(self.disk_number)
        self.row1.addLayout(self.total_disks)

        self.row2 = QHBoxLayout()
        self.track_title = LabelLinePair(
            text="Title",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="title",
        )
        self.row2.addLayout(self.track_title)

        self.row3 = QHBoxLayout()
        self.artist = LabelLinePair(
            text="Artist",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="artist",
        )
        self.row3.addLayout(self.artist)

        self.row4 = QHBoxLayout()
        self.album = LabelLinePair(
            text="Album",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="album",
        )
        self.row4.addLayout(self.album)

        self.row5 = QHBoxLayout()
        self.album_artist = LabelLinePair(
            text="Album Artist",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="albumartist",
        )
        self.row5.addLayout(self.album_artist)

        self.row6 = QHBoxLayout()
        self.year = LabelLinePair(text="Year", parent=self, width=40, value="date")
        self.bpm = LabelLinePair(text="BPM", parent=self, width=40, value="bpm")
        self.key = LabelLinePair(text="Key", parent=self, width=40, value="initialkey")
        self.row6.addLayout(self.year)
        self.row6.addLayout(self.bpm)
        self.row6.addLayout(self.key)

        self.row7 = QHBoxLayout()
        self.genre = LabelLinePair(
            text="Genre",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="genre",
        )
        # self.genre_splitter = QComboBox()
        # self.genre_splitter.addItems(["/", ",", ";"])
        self.row7.addLayout(self.genre)

        self.row8 = QHBoxLayout()
        self.comment = LabelLinePair(
            text="Comment",
            parent=self,
            width=80,
            stretch=1,
            alignment=Qt.AlignHCenter,
            value="comment",
        )
        self.row8.addLayout(self.comment)

        self.row9 = QHBoxLayout()
        self.organization = LabelLinePair(
            text="Publisher", parent=self, width=60, value="organization"
        )
        self.copyright = LabelLinePair(
            text="Copyright", parent=self, width=60, value="copyright"
        )
        self.row9.addLayout(self.organization)
        self.row9.addLayout(self.copyright)

        self.row10 = QHBoxLayout()
        self.isrc = LabelLinePair(text="ISRC", parent=self, width=60, value="isrc")
        self.location = LabelLinePair(
            text="URL", parent=self, width=60, value="location"
        )
        self.row10.addLayout(self.isrc)
        self.row10.addLayout(self.location)

        self.row11 = QHBoxLayout()
        self.lyrics = LabelTextPair(
            text="Lyrics", parent=self, width=80, value="lyrics"
        )
        self.row11.addLayout(self.lyrics)

        self.save = QPushButton("Save")
        self.save.setEnabled(False)
        self.save.clicked.connect(self.updateInfo)

        self.col1.addLayout(self.row1)
        self.col1.addLayout(self.row2)
        self.col1.addLayout(self.row3)
        self.col1.addLayout(self.row4)
        self.col1.addLayout(self.row5)
        self.col1.addLayout(self.row6)
        self.col1.addLayout(self.row7)
        self.col1.addLayout(self.row8)
        self.col1.addLayout(self.row9)
        self.col1.addLayout(self.row10)
        self.col1.addLayout(self.row11)
        self.col1.addWidget(self.save)
        self.col1.setSpacing(10)

        self.cols.addLayout(self.col1)
        self.generalLayout.addLayout(self.cols)
        self.generalLayout.setSpacing(10)

        self.setLayout(self.generalLayout)

    def showInfo(self, tracks: list[Union[FlacTrack, MP3Track]]):
        self.save.setEnabled(True)
        self.setTracks(tracks)
        for track in self.tracks:
            self.track_number.updateText(track.tracknumber)
            self.total_tracks.updateText(track.tracktotal)
            self.track_title.updateText(track.title)
            self.artist.updateText(track.artist)
            self.album.updateText(track.album)
            self.album_artist.updateText(track.albumartist)
            self.year.updateText(track.date)
            self.genre.updateText(track.genre)
            self.comment.updateText(track.comment)
            self.bpm.updateText(track.bpm)
            self.key.updateText(track.initialkey)
            self.isrc.updateText(track.isrc)
            self.organization.updateText(track.organization)
            self.copyright.updateText(track.copyright)
            self.location.updateText(track.location)
            self.lyrics.updateText(track.lyrics)

    def setTracks(self, tracks: list[Union[FlacTrack, MP3Track]]) -> None:
        self.tracks = tracks

    def updateInfo(self):
        info = {}
        info[self.track_number.getValue()] = self.track_number.getText()
        info[self.total_tracks.getValue()] = self.total_tracks.getText()
        info[self.track_title.getValue()] = self.track_title.getText()
        info[self.artist.getValue()] = self.artist.getText()
        info[self.album.getValue()] = self.album.getText()
        info[self.album_artist.getValue()] = self.album_artist.getText()
        info[self.year.getValue()] = self.year.getText()
        info[self.genre.getValue()] = self.genre.getText()
        info[self.comment.getValue()] = self.comment.getText()
        info[self.bpm.getValue()] = self.bpm.getText()
        info[self.key.getValue()] = self.key.getText()
        info[self.isrc.getValue()] = self.isrc.getText()
        info[self.organization.getValue()] = self.organization.getText()
        info[self.copyright.getValue()] = self.copyright.getText()
        info[self.location.getValue()] = self.location.getText()
        info[self.lyrics.getValue()] = self.lyrics.getText()
        print(info)
        if self.tracks is not None:
            for track in self.tracks:
                track.updateInfo(info)

    def clear(self):
        self.track_number.clear()
        self.total_tracks.clear()
        self.disk_number.clear()
        self.total_disks.clear()
        self.track_title.clear()
        self.artist.clear()
        self.album.clear()
        self.album_artist.clear()
        self.year.clear()
        self.genre.clear()
        self.bpm.clear()
        self.key.clear()
        self.comment.clear()
        self.organization.clear()
        self.copyright.clear()
        self.isrc.clear()
        self.location.clear()
        self.lyrics.clear()

    def eventFilter(self, obj: TrackLabel, event: QEvent) -> bool:
        if isinstance(obj, TrackLabel) and event.type() == 2:
            if obj.select:
                obj.setStyleSheet("")
                obj.edit.setStyleSheet("")
            else:
                obj.setStyleSheet(
                    f"background-color:{self.parentWidget().primary_color}; color:{self.parentWidget().primary_text_color}"
                )
                obj.edit.setStyleSheet(
                    f"background-color:{self.parentWidget().primary_color}; color:{self.parentWidget().primary_text_color}"
                )
            obj.select = not obj.select
            obj.edit.select = not obj.edit.select
        return super(QGroupBox, self).eventFilter(obj, event)


class PostWindow(QWidget):
    def __init__(self):
        super().__init__()

        apply_stylesheet(self, theme="dark_blue.xml")

        self.post_layout = QVBoxLayout(self)
        self.post_edit = QTextEdit(self)
        self.post_layout.addWidget(self.post_edit)
        self.setLayout(self.post_layout)
        self.setWindowTitle("Post Editor")
        self.setGeometry(400, 200, 400, 300)


class App(QWidget):
    tracks: list[Union[FlacTrack, MP3Track]]
    track: FlacTrack
    template_path: str

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        QToolTip.setFont(QFont("SansSerif", 10))

        apply_stylesheet(self, theme="dark_blue.xml")

        self.tracks = []
        self.template_path = None

        self.primary_color = os.environ.get("QTMATERIAL_PRIMARYLIGHTCOLOR")
        self.secondary_color = os.environ.get("QTMATERIAL_SECONDARYCOLOR")
        self.primary_text_color = os.environ.get("QTMATERIAL_PRIMARYTEXTCOLOR")

        self.cover_label = QLabel(self)
        self.setDefaultCover()

        self.template_lb = QLabel()
        self.select_template_btn = QPushButton("Select Template")
        self.select_template_btn.clicked.connect(self.selectTemplate)
        self.resize_cover_btn = QPushButton("Resize cover to 1200px")
        self.resize_cover_btn.clicked.connect(self.resizeCover)
        self.convert_mp3_btn = QPushButton("Convert to MP3")
        self.convert_mp3_btn.clicked.connect(self.toMp3)
        self.copy_post_btn = QPushButton("Copy Post")
        self.copy_post_btn.clicked.connect(self.copyPost)
        self.open_directory_btn = QPushButton("Open Directory")
        self.open_directory_btn.clicked.connect(self.changeDirectory)

        self.path = "E:\YandexDisk\Monstercat\Singles [MCS]"
        # self.path = "E:\Test"
        self.model = QFileSystemModel()
        self.model.setRootPath(QDir.rootPath())
        self.files = QTreeView()
        self.files.setModel(self.model)
        self.files.setRootIndex(self.model.index(self.path))
        self.files.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        self.files.clicked.connect(self.onSelected)
        self.files.selectionModel().selectionChanged.connect(self.onSelected)

        for i in range(1, self.files.model().columnCount()):
            self.files.header().hideSection(i)

        self.track_block = TrackGroupBox(title="Track Info", parent=self)

        self.main_layout = QHBoxLayout()

        self.col1 = QVBoxLayout()
        self.row1 = QHBoxLayout()
        self.row1col1 = QVBoxLayout()
        self.row1col1.addWidget(self.cover_label)
        self.row1col2 = QVBoxLayout()
        self.row1col2.addWidget(self.select_template_btn)
        self.row1col2.addWidget(self.resize_cover_btn)
        self.row1col2.addWidget(self.convert_mp3_btn)
        self.row1col2.addWidget(self.copy_post_btn)
        self.row1.addLayout(self.row1col1)
        self.row1.addLayout(self.row1col2)
        self.col1.addLayout(self.row1)
        self.col1.addWidget(self.open_directory_btn)
        self.col1.addWidget(self.files)

        self.col2 = QVBoxLayout()
        self.col2.addWidget(self.track_block)

        self.main_layout.addLayout(self.col1, stretch=1)
        self.main_layout.addLayout(self.col2, stretch=1)

        self.setLayout(self.main_layout)

        self.setGeometry(200, 150, 1000, 700)
        self.setWindowTitle("Monstercat Postmaker 2.0")
        self.show()

    def setCover(self, coverpath) -> None:
        self.cover = QPixmap(coverpath).scaled(
            350, 350, transformMode=Qt.SmoothTransformation
        )
        self.cover_label.setPixmap(self.cover)

    def setDefaultCover(self) -> None:
        self.setCover("images/cover.png")

    def getCover(self, track: Union[FlacTrack, MP3Track]) -> None:
        with open("cover.jpg", "wb") as file:
            file.write(track.cover)

    def onSelected(self):
        indexes = self.files.selectedIndexes()
        self.track_block.clear()
        self.setDefaultCover()
        self.track_block.save.setEnabled(False)
        self.tracks = []
        self.filepaths = []
        if len(indexes) != 0:
            for i, index in enumerate(indexes):
                filepath = self.model.filePath(index)
                if filepath.endswith(".flac"):
                    track = FlacTrack(filepath)
                    self.filepaths.append(filepath)
                    self.tracks.append(track)
                    if i == 0:
                        self.getCover(track)
                        self.setCover("cover.jpg")
                elif filepath.endswith(".mp3"):
                    track = MP3Track(filepath)
                    self.filepaths.append(filepath)
                    self.tracks.append(track)
                    if i == 0:
                        self.getCover(track)
                        self.setCover("cover.jpg")
            if len(self.tracks) > 0:
                self.track_block.showInfo(self.tracks)
        # print(self.tracks)

    def toMp3(self):
        for track in self.tracks:
            if isinstance(track, FlacTrack):
                track.converToMP3()

    def resizeCover(self):
        for track in self.tracks:
            if isinstance(track, FlacTrack):
                track.resizeImage(1200, 1200)

    def changeDirectory(self):
        self.path = QFileDialog.getExistingDirectory(self, "Select Folder")
        self.files.setRootIndex(self.model.index(self.path))

    def selectTemplate(self):
        self.template_path, _ = QFileDialog.getOpenFileName(
            self, "Select Template", "", "*.py"
        )

    def copyPost(self):
        if self.template_path:
            if len(self.tracks) == 1 and isinstance(self.tracks[0], FlacTrack):
                spec = importlib.util.spec_from_file_location(
                    "single_template", self.template_path
                )
                single_template = importlib.util.module_from_spec(spec)
                sys.modules["single_template"] = single_template
                spec.loader.exec_module(single_template)
                post = single_template.get_template(self.tracks[0])
                self.post_win = PostWindow()
                self.post_win.post_edit.setText(post)
                self.post_win.show()
            elif len(self.tracks) > 1 and all(
                isinstance(track, FlacTrack) for track in self.tracks
            ):
                spec = importlib.util.spec_from_file_location(
                    "single_template", self.template_path
                )
                single_template = importlib.util.module_from_spec(spec)
                sys.modules["single_template"] = single_template
                spec.loader.exec_module(single_template)
                post = single_template.get_template(Album(self.filepaths))
                self.post_win = PostWindow()
                self.post_win.post_edit.setText(post)
                self.post_win.show()


if __name__ == "__main__":

    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())
